package java_thinking.unit_15._15_10;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.RandomAccess;

/**
 * @Description :   总结与演示协变与逆变的原理
 *                  https://blog.csdn.net/li2327234939/article/details/50786860?utm_source=blogkpcl1
 *                  (1)要从泛型类取数据时，用extends；
 *                  (2)要往泛型类写数据时，用super；
 *                  (3)既要取又要写，就不用通配符(即extends与super都不用)。
 *
 *                  LSP包含以下四层含义：
 *
 *                  子类完全拥有父类的方法，且具体子类必须实现父类的抽象方法。
 *                  子类中可以增加自己的方法。
 *                  当子类覆盖或实现父类的方法时，方法的形参要比父类方法的更为宽松。
 *                  当子类覆盖或实现父类的方法时，方法的返回值要比父类更严格。
 * @author: cww
 * @DateTime: 2019-04-06 16:00
 */
class CSuper {}
class C extends CSuper {}
class CSub extends C {}

/**
 *
 * @author asd730520@live.com
 */
public class LiskovThory {
    static Number method(Number num) {
        System.out.println("num: "+ num);
        return 1;
    }

    public static void main(String[] args) {
        /**
         * 只要是Number的子类都可以作为入参<? extends Number>，形参协变
         */
        method(1);
        method(new Integer(1));
        method(0.1);

        /**
         * 只要是Number及以上的类型都可以接method的返回，<? super Number>，方法返回逆变
         */
        Object o = method(new Double(0.1));
        Number n = method(new Double(0.1));

        /**
         * 因为泛型不具备可变性，即不变性，所以可以用通配符来实现协变与逆变如下
         * class Contravariant
         * class Covariant
         */
    }
    /**
     * 这个值是随便取的，这里只是引用了Collections的copy方法为了不报错
     */
    public static final int COPY_THRESHOLD = 10;
    /**
     * java.util.Collections的copy方法(JDK1.7)完美地诠释了PECS
     * (1)要从泛型类取数据时，用extends；
     * (2)要往泛型类写数据时，用super；
     * (3)既要取又要写，就不用通配符（即extends与super都不用）。
     */
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");
        if (srcSize < COPY_THRESHOLD ||
                (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                //将目标文件的数据提取出来src.get
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                //将源文件的数据写到目标数据di.set
                di.set(si.next());
            }
        }
    }
}

/**
 *  逆变示例，定义了下界，下界以上的超类都可以引用，如方法的返回值是逆变的
 */
class Contravariant {
    /**
     * ArrayList<C>和ArrayList<CSuper>都可以赋值给下界C，也就是C以上的超类都可以
     * 但是ArrayList<CSub>就不能赋值给C
     */
    List<? super C> list = new ArrayList<C>();
    List<? super C> list1 = new ArrayList<CSuper>();


}

/**
 * 协变示例，定义了上界，上界以下的子类都可以引用，如方法的形参是协变的，
 * 数组是协变的Number[] numbers = new Integer[3]
 */
class Covariant {
    /**
     * ArrayList<C>和ArrayList<CSub>都可以赋值给上界C，也就是C的子类都可以
     * 但是ArrayList<CSuper>就不能附给C
     */
    List<? extends C> list = new ArrayList<C>();
    List<? extends C> list1 = new ArrayList<CSub>();
}
